package com.metaverse.backend.utils;

import com.metaverse.backend.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.http.Consts;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustAllStrategy;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.FormBodyPartBuilder;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;

/**
 * HTTP 帮助类<br/>
 *
 * @author chen.heng
 * @date: 2015年7月20日 下午7:16:33
 * @version 1.0
 * @since JDK 1.7
 */
@Service("httpClientUtil")
@Slf4j
public class HttpUtils implements InitializingBean, DisposableBean {


	private CloseableHttpClient httpClient;

	@Value("${httpClient.maxTotal:300}")
	private int maxTotal;

	@Value("${httpClient.defaultMaxPerRoute:100}")
	private int defaultMaxPerRoute;

	@Value("${httpClient.connectTimeout:50000}")
	private int connectTimeout;

	@Value("${httpClient.socketTimeout:50000}")
	private int socketTimeout;


	/**
	 * 默认编码
	 */
	private final String defaultCharSet = "UTF-8";

	@Override
	public void destroy() throws Exception {
		httpClient.close();
		log.info("HttpClient 关闭成功");
	}

	@Override
	public void afterPropertiesSet() {
		HttpClientBuilder builder = HttpClients.custom();
		//connectionRequestTimeout 连接不够用时等待超时时间，一定要设置。不设置的话，如果连接池连接不够用，就会线程阻塞
		RequestConfig.Builder requestConfigBuilder = RequestConfig.custom().setConnectTimeout(connectTimeout)
				.setSocketTimeout(socketTimeout).setConnectionRequestTimeout(connectTimeout);
		builder.setDefaultRequestConfig(requestConfigBuilder.build());
		try {
			SSLContext sllContext = SSLContexts.custom().loadTrustMaterial(new TrustAllStrategy()).build();
			SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sllContext, NoopHostnameVerifier.INSTANCE);
			Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
					.register("http", PlainConnectionSocketFactory.getSocketFactory())
					.register("https", sslConnectionSocketFactory)
					.build();
			PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
			cm.setMaxTotal(maxTotal);
			cm.setDefaultMaxPerRoute(defaultMaxPerRoute);
			builder.setConnectionManager(cm);
		} catch (Exception e) {
			log.error("配置sslContext 失败" , e );
		}


		this.httpClient = builder.build();
		log.info("HttpClient 初始化成功，maxTotal:{}, defaultMaxPerRoute:{}, connectTimeout:{} ms, socketTimeout:{} ms",
		maxTotal, defaultMaxPerRoute, connectTimeout, socketTimeout);

	}


	/**
	 * 获取响应编码
	 * @param responseEntity
	 * @return
	 */
	private String getContentEncoding(HttpEntity responseEntity){
		String contentEncoding = defaultCharSet;
		if(responseEntity != null){

			Header encodingHeader = responseEntity.getContentEncoding();
			if(encodingHeader != null){
				contentEncoding = encodingHeader.getValue();
			}

		}

		return contentEncoding;
	}



	/**
	 * post 请求
	 * @param url
	 * @param params
	 * @param headers
	 * @return
	 * @throws IOException
	 */
	public String doPost(String url, Map<String, ?> params, Map<String, String> headers) throws IOException {
		HttpPost httpPost = new HttpPost(url);
		String responseText = null;
		HttpEntity responseEntity = null;
		try {
			//1.参数添加
			if(params!=null && !params.isEmpty()) {
				List<NameValuePair> nvps = new ArrayList<NameValuePair>(params.size());
				Set<String> paramNames = params.keySet();
				String paramValue = null;
				for(String paramName : paramNames) {
					paramValue = Objects.toString(params.get(paramName),"");
					nvps.add(new BasicNameValuePair(paramName, paramValue));
				}
				if(!nvps.isEmpty()) {
					httpPost.setEntity(new UrlEncodedFormEntity(nvps, defaultCharSet));
				}
			}

			//2.请求头添加
			if (headers != null && !headers.isEmpty()) {
				for (Map.Entry<String, String> entry : headers.entrySet()) {
					httpPost.addHeader(entry.getKey(), entry.getValue());
				}
			}

			//3.发起请求
			long startTime = System.currentTimeMillis();
			CloseableHttpResponse response = httpClient.execute(httpPost);
			responseEntity = response.getEntity();
			long endTime = System.currentTimeMillis();
			long duration = endTime - startTime;
			if (response.getStatusLine ().getStatusCode () != 200) {
				log.error("Http请求：url:{}, 参数:{}, 编码:{}, 响应Http状态码:{} ,耗时：{}",
						url, params, defaultCharSet, response.getStatusLine().getStatusCode(), duration);
				httpPost.abort();
				throw new BusinessException("接口请求失败，响应码：" + response.getStatusLine().getStatusCode(),
						response.getStatusLine().getStatusCode());
			}

			if (responseEntity != null) {
				String contentEncoding = getContentEncoding(responseEntity);
				responseText = EntityUtils.toString(responseEntity, contentEncoding);

			}
			log.info("Http请求：url:{}, 参数:{}, 编码:{}, 响应内容:{}, 耗时：{}", url, params,
					defaultCharSet, responseText, duration);
		} finally {
			EntityUtils.consumeQuietly(responseEntity);
		}


		return responseText;
	}


	/**
	 * post 请求
	 * @param url
	 * @param params
	 * @return
	 * @throws IOException
	 */
	public String doPost(String url, Map<String, ?> params) throws IOException {
		return doPost(url, params, null);
	}

	/**
	 * post 请求
	 * @param url
	 * @return
	 * @throws IOException
	 */
	public String doPost(String url) throws IOException {
		return doPost(url, null, null);
	}

	/**
	 * post 请求
	 * @param url
	 * @param params
	 * @param headers
	 * @return
	 * @throws IOException
	 */
	public String doPostMutipartForm(String url, Map<String, ?> params, Map<String, String> headers) throws IOException {
		HttpPost httpPost = new HttpPost(url);
		String responseText = null;
		HttpEntity responseEntity = null;
		try {
			//1.参数添加
			if(params!=null && !params.isEmpty()) {
				MultipartEntityBuilder builder  = MultipartEntityBuilder.create();

				Set<String> paramNames = params.keySet();
				for(String paramName : paramNames) {
					String paramValue = Objects.toString(params.get(paramName),"");
					FormBodyPartBuilder partBuilder = FormBodyPartBuilder.create();
					partBuilder.setName(paramName);
					partBuilder.setBody(new StringBody(paramValue, "text/plain", Consts.UTF_8));
					builder.addPart(partBuilder.build());
				}

				HttpEntity httpEntity = builder.build();
				httpPost.setEntity(httpEntity);
			}

			if (headers != null && !headers.isEmpty()) {
				for (Map.Entry<String, String> entry : headers.entrySet()) {
					httpPost.addHeader(entry.getKey(), entry.getValue());
				}
			}

			//2.发起请求
			long startTime = System.currentTimeMillis();
			CloseableHttpResponse response = httpClient.execute(httpPost);
			responseEntity = response.getEntity();
			long endTime = System.currentTimeMillis();
			long duration = endTime - startTime;

			if (response.getStatusLine ().getStatusCode () != 200) {
				log.error("Http请求：url:{}, 参数:{}, 编码:{}, 响应Http状态码:{}, 耗时：{}",
						url, params, defaultCharSet, response.getStatusLine().getStatusCode(), duration);
				httpPost.abort();
				throw new BusinessException("接口请求失败，响应码：" + response.getStatusLine().getStatusCode() ,
						response.getStatusLine().getStatusCode());
			}

			if (responseEntity != null) {
				String contentEncoding = getContentEncoding(responseEntity);
				responseText = EntityUtils.toString(responseEntity, contentEncoding);

			}

			log.info("Http请求：url:{}, 参数:{}, 编码:{}, 响应内容:{}, 耗时：{}", url, params, defaultCharSet,
					responseText, duration);

		} finally {
			EntityUtils.consumeQuietly(responseEntity);
		}


		return responseText;
	}


	/**
	 *
	 * 表单数据请求
	 * @param url
	 * @param params
	 * @return
	 */
	public String doPostMutipartForm(String url, Map<String, ?> params) throws IOException {
		return doPostMutipartForm(url, params, null);
	}
	/**
	 * 请求post内容
	 * @param url
	 * @param body
	 * @param headers
	 * @param contentType
	 * @return
	 * @throws IOException
	 */
	public String doPostBody(String url, String body, Map<String, String> headers, ContentType contentType) throws IOException {
		HttpPost httpPost = new HttpPost(url);
		String responseText = null;
		HttpEntity responseEntity = null;
		try {
			//1.参数添加
			if (StringUtils.isNotBlank(body)) {
				HttpEntity entity = new StringEntity(body, contentType);
				httpPost.setEntity(entity);
			}

			if (headers != null && !headers.isEmpty()) {
				for (Map.Entry<String, String> entry : headers.entrySet()) {
					httpPost.addHeader(entry.getKey(), entry.getValue());
				}
			}

			//2.发起请求
			long startTime = System.currentTimeMillis();
			CloseableHttpResponse response = httpClient.execute(httpPost);
			responseEntity = response.getEntity();
			long endTime = System.currentTimeMillis();
			long duration = endTime - startTime;
			if (response.getStatusLine ().getStatusCode () != 200) {
				log.error("Http请求：url:{}, 参数:{}, 编码:{}, 响应Http状态码:{}, 耗时：{}",
						url, body, defaultCharSet, response.getStatusLine().getStatusCode(),duration);
				httpPost.abort();
				throw new BusinessException("接口请求失败，响应码：" + response.getStatusLine().getStatusCode() ,
						response.getStatusLine().getStatusCode());
			}

			if (responseEntity != null) {
				String contentEncoding = getContentEncoding(responseEntity);
				responseText = EntityUtils.toString(responseEntity,contentEncoding );
			}

			log.info("Http请求：url:{}, 参数:{}, 编码:{}, 响应内容:{}, 耗时：{}", url, body,
					defaultCharSet, responseText, duration);

		} finally {
			EntityUtils.consumeQuietly(responseEntity);
		}


		return responseText;

	}


	/**
	 * post 内容，json格式
	 * @param url
	 * @param jsonContent
	 * @return
	 */
	public String doPostJson(String url, String jsonContent, Map<String, String> headers) throws IOException {
		return doPostBody(url, jsonContent, headers, ContentType.APPLICATION_JSON);
	}



	/**
	 * post 内容，json格式
	 * @param url
	 * @param jsonContent
	 * @return
	 */
	public String doPostJson(String url, String jsonContent) throws IOException {
		return doPostJson(url, jsonContent, null);
	}


	/**
	 * get 请求
	 * @param url
	 * @param param
	 * @param headers
	 * @return
	 * @throws IOException
	 */
	public String doGet(String url, Map<String, ?> param, Map<String, String> headers) throws IOException {

		String resultString = "";
		HttpEntity responseEntity = null;
		try {
			URI uri = null;
			try {
				// 创建uri
				URIBuilder builder = new URIBuilder(url);
				if (param != null) {
					for (String key : param.keySet()) {
						String value = Objects.toString(param.get(key),"");
						builder.addParameter(key, value);
					}
				}
				uri = builder.build();
			}catch(URISyntaxException e){
				throw new RuntimeException("构建url失败：" + url, e);
			}


			// 创建http GET请求
			HttpGet httpGet = new HttpGet(uri);
			if (headers != null && !headers.isEmpty()) {
				for (Map.Entry<String, String> entry : headers.entrySet()) {
					httpGet.setHeader(entry.getKey(), entry.getValue());
				}
			}

			// 执行请求
			long startTime = System.currentTimeMillis();
			CloseableHttpResponse response = httpClient.execute(httpGet);
			responseEntity = response.getEntity();
			long endTime = System.currentTimeMillis();
			long duration = endTime - startTime;
			if (response.getStatusLine ().getStatusCode () != 200) {
				log.error("Http请求：url:{}, 参数:{}, 编码:{}, 响应Http状态码:{}, 耗时：{}",
						url, param, defaultCharSet, response.getStatusLine().getStatusCode(), duration);
				httpGet.abort();
				throw new BusinessException("接口请求失败，响应码：" + response.getStatusLine().getStatusCode(),
						response.getStatusLine().getStatusCode());
			}

			String contentEncoding = getContentEncoding(responseEntity);
			resultString = EntityUtils.toString(response.getEntity(), contentEncoding);
			log.info("Http请求：url:{},响应Http状态码:{}, 响应内容:{} , 耗时：{}",
					uri, response.getStatusLine().getStatusCode(), resultString, duration);
		} finally {
			EntityUtils.consumeQuietly(responseEntity);
		}


		return resultString;
	}

	/**
	 * get 请求
	 * @param url
	 * @param param
	 * @return
	 */
	public String doGet(String url, Map<String, ?> param) throws IOException {
		return doGet(url, param, null);
	}



	/**
	 * get 请求
	 * @param url
	 * @return
	 */
	public String doGet(String url) throws IOException {
		return doGet(url, null);
	}



}
